home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / strategy / mastermi.3 / mastermind.cc
C/C++ Source or Header  |  1995-04-23  |  25KB  |  878 lines

  1. /**********************************************************************
  2.   MASTERMIND player. 10 digits (0-9); words of length ln (= 2..6).
  3.  
  4.   Makefile is not provided: it's too easy to compile!
  5.  
  6.   compile like this: g++ -O -s -o mastermind mastermind.c -lncurses
  7.  
  8.   As you can see, it requires ncurses.  If you don't have this library -
  9.   get it, it's free!  On the other hand, you might have curses
  10.   instead.  Comment out the #include <ncurses/ncurses.h> line, uncomment
  11.   the next line (#include <curses.h>) and compile like this:
  12.  
  13.   g++ -O -s -o mastermind mastermind.c -lcurses -ltermcap
  14.  
  15.   Change Log:
  16.  
  17.   1.3 Dec 20 1994.  The word length is not a constant anymore.  The
  18.   players are timed. The code is cleaned up quite a bit. It should be
  19.   faster now if you have an FPU.
  20.  
  21.   1.2 Dec 12 1994.  Added demo game, menus etc.
  22.  
  23.   1.1 Nov 27 1994.  Made the guesses to be "random" numbers.  Some sort
  24.   of support for curses added.
  25.  
  26.   1.0 Nov 18 1994.  First written.
  27.  
  28.   Copyright (C) 1994 by Semion D. Shteingold, <shteingd@math.ucla.edu>
  29.   Comments, suggestions, bug reports/fixes, flame, praise are welcome!
  30.  
  31.   This program is free software; you can redistribute it and/or modify
  32.   it under the terms of the GNU General Public License as published by
  33.   the Free Software Foundation; either version 2, or (at your option)
  34.   any later version.
  35.  
  36.   This program is distributed in the hope that it will be useful, but
  37.   WITHOUT ANY WARRANTY; without even the implied warranty of
  38.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  39.   General Public License for more details.
  40.  
  41.   You should have received a copy of the GNU General Public License
  42.   along with This program; see the file COPYING.  If not, write to the
  43.   Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  44. ***********************************************************************/
  45.  
  46.  
  47. #include <stdio.h>
  48. #include <string.h>
  49. #include <stdlib.h>
  50. #include <ctype.h>
  51. #include <sys/time.h>
  52. #include <math.h>
  53. #include <unistd.h>
  54. #include <ncurses/ncurses.h>
  55. //#include <curses.h>        // If you do not have ncurses, uncomment this.
  56. #define MLN    6    // Maximal Length of the Number
  57. #define DEFLN    4    // DEFault Length of the Number
  58. #define GL    20    // Maximal Length of the Game
  59. #define VERSION "1.3"
  60. #define NUM2STR(num, st)    sprintf (st, "%.*d", ln, num)
  61.     /* Can you believe that sprintf returns <int strlen (st)> on Linux and
  62.        Solaris and <char * st> on SunOS?! */
  63. #define MINIHELP "\tq,x: quit;\th,?: help;\tb. <- cursor -> f,\t\t"
  64. #define LEFTMARGIN    5
  65. #define FIRST    15    // first column
  66. #define SECOND    55    // second column
  67. #define SPACE    10    // dist from *'s to the replies
  68. #define BSPACE    14    // dist from *'s to the beginning of the decsiption
  69. #define HELPCOL    22    // the start of help in the first line
  70. #define MSGL     (LINES-2)    // MeSsaGe Line
  71. #define ASKL     (LINES-2)    // ASK Line
  72. #define HLPL     (LINES-HLPLEN-3)    // HeLP Line
  73. #define HLPLEN    7    // Number of lines for help
  74. #define MHL     (LINES-1)    // MiniHelp Line
  75. #define STARTL    2    // START the game from Line...
  76. #define CLN_STR(a)    { move (a, 0); clrtoeol (); }
  77. #define MOVE_LEFT \
  78.     CLN_STR(MSGL); pos = (pos-1+ln)%ln; move (x, y+pos); refresh()
  79. #define MOVE_RIGHT \
  80.     CLN_STR(MSGL); pos = (pos+1)%ln; move (x, y+pos); refresh()
  81. #define MOVE_UP \
  82.     CLN_STR(MSGL); pos = (pos-1+num)%num; move(pos+STARTL,LEFTMARGIN); refresh()
  83. #define MOVE_DOWN \
  84.     CLN_STR(MSGL); pos = (pos+1)%num; move (pos+STARTL, LEFTMARGIN); refresh()
  85. #define MOVE_HOME_UP \
  86.     CLN_STR(MSGL); pos = 0; move (pos+STARTL, LEFTMARGIN); refresh()
  87. #define MOVE_HOME_LEFT \
  88.     CLN_STR(MSGL); pos = 0; move (x, y+pos); refresh()
  89. #define MOVE_END_DOWN \
  90.     CLN_STR(MSGL); pos = num-1; move (pos+STARTL, LEFTMARGIN); refresh()
  91. #define MOVE_END_RIGHT \
  92.     CLN_STR(MSGL); pos = ln-1; move (x, y+pos); refresh()
  93. #define PUT_HELP(text)    {move(0,HELPCOL); clrtoeol(); addstr(text); refresh();}
  94. #define GET_HELP(a)    CLN_STR(MSGL); help (a); refresh(); break
  95. #define PUT_STARS(x, y) \
  96.     { move(x, y); for(int i=0;i<ln;i++) addch('*'); move(x,y); refresh();}
  97. #define RND(n) ( (int) (drand48() * (n)))
  98. #ifdef _CURSES_H
  99. #define BEEP    addch (7)
  100. #endif
  101.  
  102. extern "C" double drand48();
  103. extern "C" void srand48(long);
  104.  
  105. void clear_str (int, int);
  106. void message (char *, int);
  107. void help (int);
  108. inline int not_valid (int);
  109. int get_number (int, int);
  110. int bye (int);
  111. int ask (char * , int , int );
  112. int play_one_game (int , int , double * , double * , int );
  113. void play_many_games (int , int );
  114. int menu (int , char *[]);
  115.  
  116. /*
  117.  * the length of the word. will be asked before every game.
  118.  * it is used by the class REPLY, so it has to be global. too bad...
  119.  */
  120. int ln = MLN;
  121.  
  122.  
  123. /***************************************************************
  124.  * class REPLY.
  125.  **************************************************************/
  126.  
  127. class reply {
  128.       int b, c;    // bulls & cows
  129.     public:
  130.       reply() { b = c = 0; };
  131.       ~reply(){ b = c = -1;};
  132.       void compare (int, int);    // how do these numbers match?
  133.       int match (int, int);    // do these 2 numbers match?
  134.       void output ( int, int);    // put the reply at the given position
  135.       int won() {return b == ln;};
  136.     };
  137.  
  138. int homo_guess (reply *, int *, int, int, int, int);
  139. int incr_guess (reply *, int *, int, int, int, int);
  140. int rand_guess (reply *, int *, int, int, int, int);
  141.  
  142. void reply::compare (int x, int y)
  143. // compare x and y and put the result into the REPLY
  144. {
  145.   int i, j, i10, j10;
  146.  
  147.   b = c = 0;
  148.   for (i = 0, i10 = 1 ; i < ln; i++, i10 *= 10)
  149.     for (j = 0, j10 = 1 ; j < ln; j++, j10 *= 10)
  150.       if ((x/i10)%10 == (y/j10)%10) ( (i == j) ? (b++) : (c++) );
  151. }
  152.  
  153. int reply::match (int x, int y)
  154. // check if the x and y match the REPLY
  155. {
  156.   int i, j, b1, c1, i10, j10;
  157.  
  158.   b1 = c1 = 0;
  159.   for (i = 0, i10 = 1 ; i < ln; i++, i10 *= 10)
  160.     for (j = 0, j10 = 1 ; j < ln; j++, j10 *= 10)
  161.       if ((x/i10)%10 == (y/j10)%10) ( (i == j) ? (b1++) : (c1++) );
  162.  
  163.   return (b1 == b)&& (c1 == c);
  164. }
  165.  
  166. void reply::output (int x, int y)
  167. // put the REPLY at (x, y)
  168. {
  169.   mvprintw (x, y, "b:%d c:%d", b, c);
  170.   refresh();
  171. }
  172. // end class REPLY definition.
  173.  
  174.  
  175. /*******************************************************
  176.   MAIN
  177. *******************************************************/
  178.  
  179. int main (int argc, char *argv[])
  180.   // MasterMind Player. (Byki i Korovy)
  181. {
  182.   int gwon = 0, glost = 0, gtie = 0;
  183.   time_t seed;
  184. #define MAIN_MENU    5
  185.   static char * menu_lines[MAIN_MENU] =
  186.     {"This is the Main Menu", "Play a Game of Mastermind",
  187.      "Watch a DEMO game", "Help", "Exit"};
  188.  
  189. #define PARTNER        3
  190.   static char * partner[PARTNER] =
  191.     {"Choose your partner", "Dumb incremental", "Smart random"};
  192.  
  193.   initscr();    // get the stdscr
  194.   cbreak();    // get each character immediately
  195.   noecho();    // no echo
  196.   scrollok (stdscr, FALSE);        // do NOT scroll
  197.   mvprintw (0, 0, "MasterMind %s\tHELP: ", VERSION);
  198.   srand48 (time (&seed));
  199.   mvaddstr ( MHL, LEFTMARGIN, MINIHELP );
  200.  
  201.   while (1)
  202.     {
  203.       switch ( menu (MAIN_MENU-1, menu_lines) )
  204.     {
  205.     case 0:
  206.       play_many_games (0, 1 + menu (PARTNER-1, partner ));
  207.       break;
  208.     case 1:
  209.       play_many_games (1, 2);
  210.       refresh();
  211.       break;
  212.     case 2:
  213.       help (0);
  214.       break;
  215.     case 3:
  216.       bye (0);
  217.     }
  218.     }    // of while (1)
  219. }    // of main
  220.  
  221.  
  222.  
  223. /*******************************************************************
  224.    interface stuff.
  225.    defines:
  226.     message();
  227.     ask();
  228.     clear_str();
  229.     get_number();
  230.     help();
  231.     menu();
  232.     bye();
  233. *******************************************************************/
  234.  
  235. void message (char * text, int bell)
  236.     // write the message; ring the bell.
  237. {
  238.   int x, y;
  239.  
  240.   getyx (stdscr, y, x);
  241.   CLN_STR(MSGL);
  242.   mvaddstr ( MSGL, LEFTMARGIN, text );
  243. #ifdef __NCURSES_H
  244.   if (bell) beep();
  245. #endif
  246. #ifdef _CURSES_H
  247.   if (bell) BEEP;
  248. #endif
  249.   move (y, x);
  250.   refresh();
  251.   return;
  252. }
  253.  
  254. int ask (char * text, int bell, int def)
  255.   // ask a question
  256. {
  257.   int x, y, ans;
  258.  
  259.   getyx (stdscr, y, x);
  260.   CLN_STR(ASKL);
  261.   mvaddstr ( ASKL, LEFTMARGIN, text );
  262. #ifdef __NCURSES_H
  263.   if (bell) beep();
  264. #endif
  265. #ifdef _CURSES_H
  266.   if (bell) BEEP;
  267. #endif
  268.   addstr ( (def == 0) ? " (n)..." : " (y)..." );
  269.   refresh();
  270.   ans = tolower (getch());
  271.   ans = ( ans =='y' ) || ( (def) && (ans != 'n') );
  272.   addstr ( (ans) ? "Yes." : "No.");
  273.   move (y, x);
  274.   refresh();
  275.   return (ans);
  276. }
  277.  
  278. void clear_str (int beg, int end)
  279.   // clear the specified area.
  280. {
  281.   int x, y, i;
  282.  
  283.   getyx (stdscr, y, x);
  284.   for (i = beg; i <= end; i++) CLN_STR(i);
  285.   move (y, x);
  286.   refresh();
  287.   return;
  288. }
  289.  
  290. int get_number (int x, int y)
  291.   // get an ln-digit number in_num at position (x, y).
  292. {
  293.   int in_num = 0, pos = 0, c, i, flag;
  294.   int entered[MLN];
  295.  
  296.   for (i = 0; i < ln; i++) entered[i] = 10;
  297.   move (x, y);
  298.   refresh();
  299.   while (1)
  300.     {
  301.       switch (c = tolower (getch()))
  302.     {
  303.     case ',': case 2: /* ^B */ case 'b':
  304.     case 'p': case 16: /* ^P */ case '<':
  305.       MOVE_LEFT;
  306.       break;
  307.     case '.': case 6: /* ^F */ case 'f':
  308.     case 'n': case 14: /* ^N */ case '>':
  309.       MOVE_RIGHT;
  310.       break;
  311.     case 1: /* ^A */ case 'a':
  312.       MOVE_HOME_LEFT;
  313.       break;
  314.     case 5: /* ^E */ case 'e':
  315.       MOVE_END_RIGHT;
  316.       break;
  317.     case 27: /* ESC */
  318.       switch (tolower (getch()))
  319.         {
  320.         case 'o': case 91: /* [ */
  321.           switch (tolower (getch()))
  322.         {
  323.         case 'a': case 'd':
  324.           MOVE_LEFT;
  325.           break;
  326.         case 'b': case 'c':
  327.           MOVE_RIGHT;
  328.           break;
  329.         case '7': /* home */
  330.           MOVE_HOME_LEFT;
  331.           getch();
  332.           break;
  333.         case '8': /* end */
  334.           MOVE_END_RIGHT;
  335.           getch();
  336.           break;
  337.         default:
  338.           while (getch()!='~');
  339.           GET_HELP(1);
  340.         }
  341.           break;
  342.         default:
  343.           GET_HELP(1);
  344.         }
  345.       break;
  346.     case '0': case '1': case '2': case '3': case '4':
  347.         case '5': case '6': case '7': case '8': case '9':
  348.       for (i = 0; (i < ln) && ( (c-'0') != entered[i]) ; i++);
  349.       if ( ( (c-'0') == entered[i]) && (i != ln) )
  350.         message ("You already have this digit!", 1);
  351.       else
  352.         {
  353.           entered[pos] = c-'0';
  354.           addch (c);
  355.           MOVE_RIGHT;
  356.         }
  357.       break;
  358.     case 'h': case '?':
  359.       GET_HELP(1);
  360.     case 127: /* ^? */ case 8: /* ^H */
  361.       MOVE_LEFT;
  362.         case    32:
  363.       addch ('*'); entered[pos] = 10; move (x, y+pos); break;
  364.     case    10:    // Enter
  365.       for (in_num = i = 0, flag = 1; (i < ln) && (flag) ; i++)
  366.         if (entered[i] == 10) flag = 0;
  367.         else in_num = in_num*10+entered[i];
  368.       if (flag)
  369.         {
  370.           CLN_STR(MSGL);
  371.           return (in_num);
  372.         }
  373.       else
  374.         {
  375.           in_num = 0;
  376.           message ("You have not entered the number yet!", 1);
  377.         }
  378.       break;
  379.     case 'q': case 'x': case 3: /* ^C */
  380.       if (ask ("Are you sure you want to quit?", 0, 0)) return (-1);
  381.     case 12: /* ^L */ case 18: /* ^R */
  382.       // redrawln (stdscr, 0, 24); break;
  383.       message ("sorry, cannot redraw screen yet", 1); break;
  384.     default:
  385.       message ("Illegal key. Please use \"h\" or \"?\" for help.", 1);
  386.       break;
  387.     }
  388.       refresh();
  389.     }    // end of while (1)
  390. } // get_number
  391.  
  392. void help (int num)
  393. /*******************************************
  394.    give help according to the context:
  395.     0: general;
  396.     1: get_number;
  397.     2: license;
  398.     3: send e-mail to the author;
  399.    then return to the initial position of the cursor.
  400. *******************************************/
  401. {
  402.   int x, y;
  403.  
  404.   getyx (curscr, x, y);
  405.   clear_str (HLPL, MHL);
  406.   mvaddstr ( MHL, 0, "G - general help; also: N; L; M." );
  407.   switch (num)
  408.     {
  409.     case 0:
  410.       clear_str (HLPL, HLPL+HLPLEN);
  411.       mvaddstr ( HLPL, 0, "\tHELP: General:\n\t\
  412. This is a game. You and I think of a number, and each tries\n\t\
  413. to guess the number of the other. The answer is displayed\n\t\
  414. to the right in the form: \"b:2 c:1\". This means that\n\t\
  415. the number to the left has 3 common digits with the number\n\t\
  416. being guessed, 2 of them on the right position and one on\n\t\
  417. on the wrong.");
  418.       message ("Press any key to continue...", 0);
  419.       getch();
  420.       clear_str (HLPL+1, HLPL+HLPLEN);
  421.       mvaddstr ( HLPL+1, 0 , "\t\
  422. More help: N - how to enter a number; L - see the license;\n\t\
  423. M - send mail to the author.\n\t\
  424. Beware: sending mail will terminate the program!\n\t\
  425. G - see the general help again.");
  426.       refresh();
  427.       CLN_STR(MSGL);
  428.       break;
  429.     case 1:
  430.       clear_str (HLPL, HLPL+HLPLEN);
  431.       mvaddstr ( HLPL, 0 , "\tHELP: Entering a number:\n\t\
  432. You should enter a \"valid\" number.\n\t\
  433. A number is \"valid\" if it contains digits \'0\' to \'9\'\n\t\
  434. and does not have the same digit twice. The number can\n\t\
  435. start with 0. Use the space bar to erase the curent digit,\n\t\
  436. and the <,b, and >,f keys to move within the allotted space.\n\t\
  437. You can use the BS/DEL keys. The arrows should also work.");
  438.       refresh();
  439.       break;
  440.     case 2:
  441.       clear_str (HLPL, HLPL+HLPLEN);
  442.       mvaddstr ( HLPL, 0, "\tHELP: License information:\n\t\
  443. Copyright (C) 1994 by Sam Shteingold, <shteingd@math.ucla.edu>\n\t\
  444. Comments, suggestions, bug reports/fixes are welcome!\n\t\
  445. This program is free software; you can redistribute it and/or modify\n\t\
  446. it under the terms of the GNU General Public License as published by\n\t\
  447. the Free Software Foundation; either version 2, or (at your option)\n\t\
  448. any later version.");
  449.       message ("Press any key to continue...", 0);
  450.       getch();
  451.       clear_str (HLPL+1, HLPL+HLPLEN);
  452.       mvaddstr ( HLPL+1, 0, "\t\
  453. This program is distributed in the hope that it will be useful, but\n\t\
  454. WITHOUT ANY WARRANTY; without even the implied warranty of\n\t\
  455. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n\t\
  456. General Public License for more details.\n\t\
  457. You should have received a copy of the GNU General Public License\n\t\
  458. along with This program; see the file COPYING.  If not, write to the\n\t\
  459. Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.");
  460.       refresh();
  461.       CLN_STR(MSGL);
  462.       break;
  463.     case 3:
  464.       nocbreak();
  465.       echo();
  466.       clear ();
  467.       mvaddstr (0,0, "Enter the message. Terminate by a single dot on the line.\n");
  468.       refresh();
  469.       system ("mail shteingd@math.ucla.edu -s \"I use mastermind...\"");
  470. //      cbreak(); noecho(); redrawwin (stdscr); break;
  471.       bye (0);
  472.     } // of switch (num);
  473.   switch (tolower (getch()))
  474.     {
  475.     case 'g':
  476.       help (0); break;
  477.     case 'n':
  478.       help (1); break;
  479.     case 'l':
  480.       help (2); break;
  481.     case 'm':
  482.       help (3); break;
  483.     default:
  484.       clear_str (HLPL, LINES);
  485.       mvaddstr (MHL, LEFTMARGIN, MINIHELP);
  486.     }
  487.   move (x, y);
  488.   refresh();
  489.   return;
  490. } // help
  491.  
  492. int menu (int num, char * items[])
  493.   // chose an item in the menu
  494.   // num items; items[0] = title; others - lables of the items
  495. {
  496.   int pos = 0, c, i;
  497.  
  498.   clear_str (1, MSGL);
  499.   PUT_HELP(items[0]);
  500.   for (i = 1; i <= num; i++)
  501.     mvprintw (i + STARTL - 1 , LEFTMARGIN, "%d. %s", i, items[i]);
  502.   move (STARTL, LEFTMARGIN);
  503.   refresh();
  504.   while (1)
  505.     {
  506.       switch (c = tolower (getch()))
  507.     {
  508.     case ',': case 16: /* ^P */ case 2: /* ^B */ case 'b': case 'p':
  509.     case 'k': case 127: /* ^? */ case 8: /* ^H */ case '<':
  510.       MOVE_UP;    break;
  511.     case '.': case 14: /* ^N */ case 6: /* ^F */ case 'f': case 'n':
  512.     case 'j': case 32: /* SP */ case '>':
  513.       MOVE_DOWN;    break;
  514.     case 1: /* ^A */ case 'a':
  515.       MOVE_HOME_UP;    break;
  516.     case 5: /* ^E */ case 'e':
  517.       MOVE_END_DOWN;    break;
  518.     case 27: /* ESC */
  519.       switch (tolower (getch()))
  520.         {
  521.         case 'o': case 91: /* [ */
  522.           switch (tolower (getch()))
  523.         {
  524.         case 'a': /* ^ */ case 'd': /* < */
  525.           MOVE_UP;
  526.           break;
  527.         case 'b': /* > */ case 'c': /* v */
  528.           MOVE_DOWN;
  529.           break;
  530.         case '7': /* home */
  531.           MOVE_HOME_UP;
  532.           getch();
  533.           break;
  534.         case '8': /* end */
  535.           MOVE_END_DOWN;
  536.           getch();
  537.           break;
  538.         default:
  539.           while (getch() != '~');
  540.           GET_HELP(0);
  541.         }
  542.           break;
  543.         default:
  544.           GET_HELP(0);
  545.         }
  546.       break;
  547.     case 'h': case '?':
  548.       GET_HELP(0);
  549.     case    10:    // Enter
  550.       return (pos);
  551.     case 'q': case 'x': case 3: /* ^C */
  552.       bye (0);
  553.     case 12: /* ^L */ case 18: /* ^R */
  554.       //redrawln (stdscr, 0, 24); break;
  555.       message ("sorry, cannot redraw screen yet", 1);
  556.       break;
  557.     case '1': case '2': case '3': case '4':
  558.         case '5': case '6': case '7': case '8': case '9':
  559.       if (c-'1' < num) return (c-'1');
  560.     default:
  561.       message ("Illegal key. Please use \"h\" or \"?\" for help.", 1);
  562.       break;
  563.     }
  564.       refresh();
  565.     }
  566. } // menu
  567.  
  568. int bye (int i)
  569. {
  570.   CLN_STR(LINES-1);
  571.   nocbreak();
  572.   echo();
  573.   move (LINES-1, 0);
  574.   refresh();
  575.   endwin();
  576.   exit (i);
  577. }
  578.  
  579. // end of interface
  580.  
  581.  
  582. /*******************************************************************
  583.    algorithm only. Uses:
  584.     message();
  585.     get_number().
  586.     ask();
  587. *******************************************************************/
  588.  
  589. int play_one_game (int first, int second, double * f_tm, double * s_tm,
  590.            int max_num)
  591. /**************
  592.    play Mastermind just once.
  593.    first/second:
  594.     0: human; first AND second cannot be 0 BOTH at the same time;
  595.     1: computer, incremental;
  596.     2: computer, random.
  597.    returns:
  598.     0: tie;
  599.     1: first won;
  600.     -1: second won;
  601.     10: error.
  602. **************/
  603. {
  604.   struct timeval tm1, tm2;
  605.   struct timezone tzp;
  606.   int frs_num, sec_num, round;
  607.   reply frs_reps[GL], sec_reps[GL];
  608.   int frs_ask[GL], sec_ask[GL];
  609.   int (*frs_get) (reply *, int *, int, int, int, int);
  610.   int (*sec_get) (reply *, int *, int, int, int, int);
  611.  
  612. #define NUMLN    5
  613.   static char * lines[NUMLN] = {"Are you still there?! You are very daring!",
  614.             "No result yet, but somehow I'm sure I'll win.",
  615.             "You think slowly. I bet you are a mere human!",
  616.             "Your case is hopeless. Surrender!",
  617.             "Why don\'t you just give up?!"};
  618.  
  619.   if ( (first == 0) && (second == 0))
  620.     {
  621.       message ("both cannot be humans!", 1);
  622.       return 10;
  623.     }
  624.  
  625.   clear_str (STARTL, MSGL-1);
  626.  
  627.   // the framework:
  628.   switch (first)
  629.     {
  630.     case 0:
  631.       mvaddstr (STARTL,        FIRST-BSPACE,    "1:Human - #:");
  632.       mvaddstr (STARTL+1,    SECOND-BSPACE,    "  H guesses:");
  633.       frs_get = & homo_guess;
  634.       break;
  635.     case 1:
  636.       mvaddstr (STARTL,        FIRST-BSPACE,    "1:CIncr - #:");
  637.       mvaddstr (STARTL+1,    SECOND-BSPACE,    " CI guesses:");
  638.       frs_get = & incr_guess;
  639.       break;
  640.     case 2:
  641.       mvaddstr ( STARTL,    FIRST-BSPACE,    "1:CRand - #:");
  642.       mvaddstr ( STARTL+1,    SECOND-BSPACE,    " CR guesses:");
  643.       frs_get = & rand_guess;
  644.       break;
  645.     default:
  646.       message ("wrong first argument in play_one_game. hit a key\t\t", 1);
  647.       getch();
  648.       return (10);
  649.     }
  650.   PUT_STARS(STARTL, FIRST)
  651.   switch (second)
  652.     {
  653.     case 0:
  654.       mvaddstr (STARTL,        SECOND-BSPACE,    "2:Human - #:");
  655.       mvaddstr (STARTL+1,    FIRST-BSPACE,    "  H guesses:");
  656.       sec_get = & homo_guess;
  657.       break;
  658.     case 1:
  659.       mvaddstr (STARTL,        SECOND-BSPACE,    "2:CIncr - #:");
  660.       mvaddstr (STARTL+1,    FIRST-BSPACE,    " CI guesses:");
  661.       sec_get = & incr_guess;
  662.       break;
  663.     case 2:
  664.       mvaddstr (STARTL,        SECOND-BSPACE,    "2:CRand - #:");
  665.       mvaddstr (STARTL+1,    FIRST-BSPACE,    " CR guesses:");
  666.       sec_get = & rand_guess;
  667.       break;
  668.     default:
  669.       message ("wrong second argument in play_one_game. hit a key\t\t", 1);
  670.       getch();
  671.       return (10);
  672.     }
  673.   PUT_STARS (STARTL, SECOND)
  674.  
  675.   // get the numbers to be guessed.
  676.   if (first)
  677.     {
  678.       do frs_num = RND(max_num);
  679.       while (not_valid (frs_num));
  680.     }
  681.   else
  682.     {
  683.       PUT_HELP("Enter your secret number, then press <Enter>.");
  684.       if ( (frs_num = get_number (STARTL, FIRST)) == -1) return (10);
  685.     }
  686.   if (second)
  687.     {
  688.       do sec_num = RND(max_num);
  689.       while (not_valid (sec_num));
  690.     }
  691.   else
  692.     {
  693.       PUT_HELP("Enter your secret number, then press <Enter>.");
  694.       if ( (sec_num = get_number (STARTL, SECOND)) == -1) return (10);
  695.     }
  696.  
  697.   round = 0;
  698.   do
  699.     {
  700.       // do the moves:
  701.       gettimeofday(&tm1, &tzp);
  702.       if ( ( (*frs_get) (frs_reps, frs_ask, SECOND,
  703.             round, max_num, sec_num)) == -1)
  704.     return (10);
  705.       gettimeofday(&tm2, &tzp);
  706.       *f_tm += (double) (tm2.tv_sec - tm1.tv_sec) +
  707.      ( (double) (tm2.tv_usec - tm1.tv_usec) )/1000000;
  708.       refresh();
  709.  
  710.       gettimeofday(&tm2, &tzp);
  711.       if ( ( (*sec_get) (sec_reps, sec_ask, FIRST ,
  712.             round, max_num, frs_num)) == -1)
  713.     return (10);
  714.       gettimeofday(&tm2, &tzp);
  715.       *s_tm += (double) (tm2.tv_sec - tm1.tv_sec) +
  716.      ( (double) (tm2.tv_usec - tm1.tv_usec) ) /1000000;
  717.       refresh();
  718.  
  719.       // the results of the round:
  720.       if ( (frs_reps[round].won())&& (sec_reps[round].won())) return (0); //tie
  721.       else if ( (! (frs_reps[round].won()))&& (sec_reps[round].won()))
  722.     {
  723.       mvprintw ( STARTL, SECOND, "%.*d", ln, sec_num);
  724.       refresh();
  725.       return (-1);
  726.     }
  727.       else if ( (frs_reps[round].won())&& (! (sec_reps[round].won())))
  728.     {
  729.       mvprintw (STARTL, FIRST, "%.*d", ln, frs_num);
  730.       refresh();
  731.       return (1);
  732.     }
  733.       else if (! (first*second)) message (lines[RND(NUMLN)], 0);
  734.     }
  735.   while (round++ < GL);
  736. } // of play_one_game
  737.  
  738. int rand_guess (reply reps[GL], int old_guess[GL], int pos,
  739.         int round, int max_num, int secret)
  740. /********
  741.   make a random guess of SECRET, which is up to MAX_NUM; put at collumn POS.
  742.   use old guesses OLD_GUESS with replies REPS in ROUND.
  743. ********/
  744. {
  745.   int j, i, ini, num_ok = 0;
  746.  
  747.   ini = RND(max_num);
  748.   PUT_STARS(STARTL+1+round, pos)
  749.   message ("CRand is thinking. Please wait...", 0);
  750.   if (drand48() > 0.5)
  751.     {    // go up
  752.       for (i = ini+1; (i != ini) && (!num_ok) ; i = ( i + 1 ) % max_num )
  753.     if (!not_valid (i))
  754.       for (j = 0, num_ok = 1 ; (j < round) && (num_ok) ; j++)
  755.         num_ok *= reps[j].match (i, old_guess[j]);
  756.       old_guess[round] = ( i - 1 + max_num ) % max_num;
  757.     }
  758.   else
  759.     {    // go down
  760.       for (i = ini-1; (i != ini) && (!num_ok) ; i = ( i - 1 + max_num ) % max_num )
  761.     if (!not_valid (i))
  762.       for (j = 0, num_ok = 1 ; (j < round) && (num_ok) ; j++)
  763.         num_ok *= reps[j].match (i, old_guess[j]);
  764.       old_guess[round] = ( i + 1 ) % max_num;
  765.     }
  766.   if (i == ini) { message ("Random: ERROR! hit a key", 1); getch(); }
  767.   mvprintw (STARTL+1+round, pos, "%.*d", ln, old_guess[round]);
  768.   reps[round].compare (old_guess[round], secret);
  769.   reps[round].output (STARTL+1+round, pos+SPACE);
  770.   return (old_guess[round]);
  771. }    // rand_guess
  772.  
  773. int incr_guess (reply reps[GL], int old_guess[GL], int pos,
  774.         int round, int max_num, int secret)
  775. /*****
  776.   make an incremental guess of SECRET, which is up to MAX_NUM; put at
  777.   collumn POS.  use old guesses OLD_GUESS with replies REPS in ROUND.
  778. *****/
  779. {
  780.   int j, i, ini, num_ok = 0;
  781.  
  782.   PUT_STARS(STARTL+1+round, pos)
  783.   message ("CIncr is thinking. Please wait...", 0);
  784.   (round) ? (i = old_guess[round-1]) : (i = 0);
  785.   for (i++, num_ok = 0; (i < max_num) && (!num_ok); i++)
  786.     if (!not_valid (i))
  787.       for (j = 0, num_ok = 1; (j < round) && (num_ok) ; j++)
  788.     num_ok *= reps[j].match (i, old_guess[j]);
  789.   if (i == max_num) { message ("Incremental: ERROR! hit a key", 1); getch(); }
  790.   old_guess[round] = --i;
  791.   mvprintw (STARTL+1+round, pos, "%.*d", ln, old_guess[round]);
  792.   reps[round].compare (old_guess[round], secret);
  793.   reps[round].output (STARTL+1+round, pos+SPACE);
  794.   return (old_guess[round]);
  795. }    // incr_guess
  796.  
  797. int homo_guess (reply reps[GL], int old_guess[GL], int pos,
  798.         int round, int max_num, int secret)
  799. // get the human player's guess.
  800. {
  801.   PUT_STARS(STARTL+1+round, pos)
  802.   PUT_HELP("Enter your guess, then press <Enter>.");
  803.   old_guess[round] = get_number (STARTL+1+round, pos);
  804.   reps[round].compare (old_guess[round], secret);
  805.   reps[round].output (STARTL+1+round, pos+SPACE);
  806.   return (old_guess[round]);
  807. }
  808.  
  809. void play_many_games (int f, int s)
  810. // play several games, keeping the score.
  811. {
  812.   int first = 0, second = 0, tie = 0, i;
  813.   int max_num = 1;    // 10^ln
  814.   double f_tm = 0, s_tm = 0;
  815.  
  816.   // get ln:
  817.   PUT_HELP ("What will the length of the number be?");
  818.   clear_str (STARTL, MSGL);
  819.   mvaddstr (HLPL, 0, "\tPlease enter the length of the word.\n\t\
  820. You should enter a reasonable number:\n\t\
  821. 2 is too short - it won't be interesting, \n\t\
  822. 6 is too long - it'll take me too long\n\t\
  823. to produce each guess.\n\t\
  824. The reasonable number is, probably, 4, \n\t\
  825. but 3 also might be interesting.");
  826.   mvprintw ( MSGL, LEFTMARGIN,
  827.         "Enter number from 2 to %d. Default is %d ...", MLN, DEFLN );
  828.   refresh();
  829.   ln = getch() - '0';
  830.   if ( (ln < 2) || (ln > MLN)) ln = DEFLN;
  831.   clear_str (HLPL, MSGL);
  832.   // if not a demo, get_number will say something...
  833.   PUT_HELP("Demo game...");
  834.  
  835.   // initialization of 'max_num'.
  836.   for (i = 0; i < ln; i++) max_num *= 10;
  837.  
  838.   do
  839.     {
  840.       switch (play_one_game (f, s, & f_tm, & s_tm, max_num))
  841.     {
  842.     case 0 :
  843.       if (f*s == 0) PUT_HELP ("Uphhh... Tie! That was close!");
  844.       tie++ ; break;
  845.     case 1 :
  846.       if (f == 0) PUT_HELP ("Ouch! You are SMART! You beat me!");
  847.       if (s == 0) PUT_HELP ("As one could expect, you lost.");
  848.       first++; break;
  849.     case -1:
  850.       if (s == 0) PUT_HELP ("Ouch! You are SMART! You beat me!");
  851.       if (f == 0) PUT_HELP ("As one could expect, you lost.");
  852.       second++; break;
  853.     }
  854.       mvprintw (1, 1, "1:%d; 2:%d; tie:%d; games:%d; time: 1:%.3f sec; 2:%.3f sec.",
  855.           first, second, tie, first+second+tie, f_tm, s_tm);
  856.       mvprintw (STARTL, FIRST + SPACE, "(won: %d)", first);
  857.       mvprintw (STARTL, SECOND + SPACE, "(won: %d)", second);
  858.     }
  859.   while (ask ("The game is over. Again?", 0, 1));
  860.   clear_str (STARTL-1, MSGL);
  861. }    // play_many_games
  862.  
  863. inline int not_valid (int x)
  864. /***************************************
  865.   Check if the number 'x' is valid. Return 0 if valid; or the second
  866.   part or the first bad spot: for number 4598595 returns 4 - the
  867.   position of the second "5".
  868.   To save time does NOT check x < 10^ln. BEWARE!
  869. ***************************************/
  870. {
  871.   int i, j, i10, j10;
  872.  
  873.   for (i = 1, i10 = 10 ; i < ln; i++, i10 *= 10)
  874.     for (j = 0, j10 = 1 ; j < i; j++, j10 *= 10)
  875.       if ( (x/i10)%10 == (x/j10)%10 ) return (i);
  876.   return (0);
  877. }
  878.